<template>
  <div class="container">
    <div class="panel">
      <div class="hud">
        <div class="stats">
          <div class="stat">分数 <strong id="score">0</strong></div>
          <div class="stat">最高分 <strong id="best">0</strong></div>
          <div class="stat">速度 <strong id="speed">中</strong></div>
        </div>
        <div class="controls">
          <button class="btn primary" id="btnStart">开始 / 暂停</button>
          <button class="btn" id="btnRestart">重新开始</button>
          <button class="btn" id="btnSpeed">切换速度</button>
          <button class="btn danger" id="btnEdge">穿墙: 开</button>
        </div>
      </div>
      <div class="board-wrap">
        <canvas id="board" width="600" height="600" aria-label="贪吃蛇棋盘" role="img"></canvas>
      </div>
    </div>

    <aside class="panel sidebar">
      <div>
        <h1>贪吃蛇</h1>
        <p>方向键 / WASD 控制蛇移动，吃到食物增长并加分。支持暂停、重开、速度切换与穿墙模式。</p>
      </div>

      <div>
        <p>键位</p>
        <div class="keycaps">
          <div class="key">↑ / W</div>
          <div class="key">← / A</div>
          <div class="key">↓ / S</div>
          <div class="key">→ / D</div>
          <div class="key">空格：开始/暂停</div>
          <div class="key">R：重开</div>
          <div class="key">E：穿墙开关</div>
          <div class="key">1/2/3：速档</div>
        </div>
      </div>

      <div>
        <p>触屏方向键</p>
        <div class="touch">
          <span class="spacer"></span>
          <div class="dpad" data-dir="up">↑</div>
          <span class="spacer"></span>
          <div class="dpad" data-dir="left">←</div>
          <span class="spacer"></span>
          <div class="dpad" data-dir="right">→</div>
          <span class="spacer"></span>
          <div class="dpad" data-dir="down">↓</div>
          <span class="spacer"></span>
        </div>
      </div>

      <div class="footer">本地最高分保存在浏览器中。</div>
    </aside>
  </div>
</template>

<script setup>
import { onMounted, ref } from 'vue'

onMounted(() => {
  const canvas = document.getElementById('board');
  const ctx = canvas.getContext('2d');
  const scoreEl = document.getElementById('score');
  const bestEl = document.getElementById('best');
  const speedEl = document.getElementById('speed');
  const btnStart = document.getElementById('btnStart');
  const btnRestart = document.getElementById('btnRestart');
  const btnSpeed = document.getElementById('btnSpeed');
  const btnEdge = document.getElementById('btnEdge');

  const GRID_SIZE = 20; // 20x20 网格
  const CELL = canvas.width / GRID_SIZE; // 每格像素

  const SPEED_PRESET = [140, 90, 60]; // 毫秒/步（慢/中/快）
  let speedIndex = 1;

  const bestKey = 'snake.bestScore.v1';
  let bestScore = Number(localStorage.getItem(bestKey) || 0);
  bestEl.textContent = String(bestScore);
  speedEl.textContent = ['慢', '中', '快'][speedIndex];

  /** 游戏状态 */
  let snake = [];
  let direction = { x: 1, y: 0 }; // 初始向右
  let nextDirection = { x: 1, y: 0 };
  let food = null;
  let score = 0;
  let running = false;
  let gameOver = false;
  let wrapEdge = true; // 穿墙
  let lastStepAt = 0;

  function initializeGame() {
    const startX = Math.floor(GRID_SIZE / 3);
    const startY = Math.floor(GRID_SIZE / 2);
    snake = [
      { x: startX + 2, y: startY },
      { x: startX + 1, y: startY },
      { x: startX, y: startY },
    ];
    direction = { x: 1, y: 0 };
    nextDirection = { x: 1, y: 0 };
    score = 0;
    scoreEl.textContent = '0';
    gameOver = false;
    placeFood();
    draw();
  }

  function placeFood() {
    while (true) {
      const x = Math.floor(Math.random() * GRID_SIZE);
      const y = Math.floor(Math.random() * GRID_SIZE);
      if (!snake.some(s => s.x === x && s.y === y)) {
        food = { x, y };
        return;
      }
    }
  }

  function step(now) {
    if (!running) return;
    if (!lastStepAt) lastStepAt = now;
    const interval = SPEED_PRESET[speedIndex];
    if (now - lastStepAt < interval) {
      requestAnimationFrame(step);
      return;
    }
    lastStepAt = now;

    direction = nextDirection; // 应用上一次输入

    const head = snake[0];
    let newX = head.x + direction.x;
    let newY = head.y + direction.y;

    if (wrapEdge) {
      if (newX < 0) newX = GRID_SIZE - 1;
      if (newX >= GRID_SIZE) newX = 0;
      if (newY < 0) newY = GRID_SIZE - 1;
      if (newY >= GRID_SIZE) newY = 0;
    } else {
      if (newX < 0 || newX >= GRID_SIZE || newY < 0 || newY >= GRID_SIZE) {
        endGame();
        draw();
        return;
      }
    }

    const newHead = { x: newX, y: newY };
    if (snake.some((s, i) => i !== 0 && s.x === newHead.x && s.y === newHead.y)) {
      endGame();
      draw();
      return;
    }

    snake.unshift(newHead);
    const ate = food && newHead.x === food.x && newHead.y === food.y;
    if (ate) {
      score += 1;
      scoreEl.textContent = String(score);
      if (score > bestScore) {
        bestScore = score;
        localStorage.setItem(bestKey, String(bestScore));
        bestEl.textContent = String(bestScore);
      }
      placeFood();
    } else {
      snake.pop();
    }

    draw();
    requestAnimationFrame(step);
  }

  function endGame() {
    running = false;
    gameOver = true;
  }

  function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 背景网格
    ctx.save();
    ctx.strokeStyle = getComputedStyle(document.documentElement).getPropertyValue('--grid') || '#20253f';
    ctx.lineWidth = 1;
    for (let i = 1; i < GRID_SIZE; i++) {
      const p = Math.floor((canvas.width / GRID_SIZE) * i) + 0.5;
      ctx.beginPath(); ctx.moveTo(p, 0); ctx.lineTo(p, canvas.height); ctx.stroke();
      ctx.beginPath(); ctx.moveTo(0, p); ctx.lineTo(canvas.width, p); ctx.stroke();
    }
    ctx.restore();

    // 食物
    if (food) {
      drawCell(food.x, food.y, '#f59e0b');
      drawCircle(food.x, food.y, '#fde68a');
    }

    // 蛇
    for (let i = snake.length - 1; i >= 0; i--) {
      const seg = snake[i];
      const t = i / Math.max(1, snake.length - 1);
      const color = lerpColor('#22d3ee', '#4ade80', t);
      drawRounded(seg.x, seg.y, color);
    }

    if (!running) {
      ctx.save();
      ctx.fillStyle = 'rgba(0,0,0,0.35)';
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      ctx.fillStyle = '#e5e7eb';
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      ctx.font = 'bold 28px system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial';
      ctx.fillText(gameOver ? '游戏结束 - 按 R 重新开始' : '按 空格 开始', canvas.width / 2, canvas.height / 2);
      ctx.restore();
    }
  }

  function drawCell(x, y, color) {
    const pad = 2;
    ctx.fillStyle = color;
    ctx.fillRect(x * CELL + pad, y * CELL + pad, CELL - pad * 2, CELL - pad * 2);
  }

  function drawRounded(x, y, color) {
    const pad = 2;
    const r = 6;
    const px = x * CELL + pad;
    const py = y * CELL + pad;
    const w = CELL - pad * 2;
    const h = CELL - pad * 2;
    ctx.beginPath();
    ctx.moveTo(px + r, py);
    ctx.arcTo(px + w, py, px + w, py + h, r);
    ctx.arcTo(px + w, py + h, px, py + h, r);
    ctx.arcTo(px, py + h, px, py, r);
    ctx.arcTo(px, py, px + w, py, r);
    ctx.closePath();
    ctx.fillStyle = color;
    ctx.fill();
    ctx.fillStyle = 'rgba(255,255,255,0.08)';
    ctx.fillRect(px, py, w, h * 0.45);
  }

  function drawCircle(x, y, color) {
    const cx = x * CELL + CELL / 2;
    const cy = y * CELL + CELL / 2;
    ctx.beginPath();
    ctx.arc(cx, cy, CELL * 0.22, 0, Math.PI * 2);
    ctx.fillStyle = color;
    ctx.fill();
  }

  function hexToRgb(hex) {
    const h = hex.replace('#', '');
    const bigint = parseInt(h, 16);
    if (h.length === 6) return { r: (bigint >> 16) & 255, g: (bigint >> 8) & 255, b: bigint & 255 };
    return { r: 0, g: 0, b: 0 };
  }
  function lerp(a, b, t) { return a + (b - a) * t; }
  function lerpColor(c1, c2, t) {
    const a = hexToRgb(c1), b = hexToRgb(c2);
    const r = Math.round(lerp(a.r, b.r, t));
    const g = Math.round(lerp(a.g, b.g, t));
    const b2 = Math.round(lerp(a.b, b.b, t));
    return `rgb(${r}, ${g}, ${b2})`;
  }

  function setDirection(dx, dy) {
    // 防止直接反向
    if (dx === -direction.x && dy === -direction.y) return;
    nextDirection = { x: dx, y: dy };
  }

  // 事件绑定
  window.addEventListener('keydown', (e) => {
    if (e.code === 'ArrowUp' || e.key === 'w' || e.key === 'W') { setDirection(0, -1); e.preventDefault(); }
    if (e.code === 'ArrowDown' || e.key === 's' || e.key === 'S') { setDirection(0, 1); e.preventDefault(); }
    if (e.code === 'ArrowLeft' || e.key === 'a' || e.key === 'A') { setDirection(-1, 0); e.preventDefault(); }
    if (e.code === 'ArrowRight' || e.key === 'd' || e.key === 'D') { setDirection(1, 0); e.preventDefault(); }

    if (e.code === 'Space') { toggleStart(); e.preventDefault(); }
    if (e.key === 'r' || e.key === 'R') { restart(); }
    if (e.key === 'e' || e.key === 'E') { toggleEdge(); }
    if (e.key === '1') setSpeedIndex(0);
    if (e.key === '2') setSpeedIndex(1);
    if (e.key === '3') setSpeedIndex(2);
  }, { passive: false });

  document.querySelectorAll('.dpad').forEach(btn => {
    const dir = btn.getAttribute('data-dir');
    const map = { up: [0, -1], down: [0, 1], left: [-1, 0], right: [1, 0] };
    const onPress = (ev) => { ev.preventDefault(); const [dx, dy] = map[dir]; setDirection(dx, dy); };
    btn.addEventListener('mousedown', onPress);
    btn.addEventListener('touchstart', onPress, { passive: false });
  });

  btnStart.addEventListener('click', toggleStart);
  btnRestart.addEventListener('click', restart);
  btnSpeed.addEventListener('click', () => setSpeedIndex((speedIndex + 1) % SPEED_PRESET.length));
  btnEdge.addEventListener('click', toggleEdge);

  function setSpeedIndex(idx) {
    speedIndex = idx;
    speedEl.textContent = ['慢', '中', '快'][speedIndex];
  }

  function toggleEdge() {
    wrapEdge = !wrapEdge;
    btnEdge.textContent = `穿墙: ${wrapEdge ? '开' : '关'}`;
  }

  function toggleStart() {
    if (gameOver) return;
    running = !running;
    if (running) {
      lastStepAt = 0;
      requestAnimationFrame(step);
    }
    draw();
  }

  function restart() {
    running = false;
    gameOver = false;
    lastStepAt = 0;
    initializeGame();
  }

  // 初始
  initializeGame();
  draw();
  // 根据容器自适应像素密度
  const resize = () => {
    const rect = canvas.getBoundingClientRect();
    const size = Math.round(Math.min(rect.width, rect.height));
    const dpr = Math.min(2, window.devicePixelRatio || 1);
    canvas.width = Math.floor(size * dpr);
    canvas.height = Math.floor(size * dpr);
    // 重新计算单元大小
    // 注意：这里通过变量 CELL 重新赋值不可行（const），所以临时缩放绘制
    // 采用上下文缩放使得逻辑坐标仍按 600px 设计图绘制
    const base = 600;
    ctx.setTransform(canvas.width / base, 0, 0, canvas.height / base, 0, 0);
    draw();
  };
  window.addEventListener('resize', resize);
  resize();
})
</script>

<style scoped>
@import './style.css';
</style>
